/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: porxy_event.h
 *
 * Purpose: the event base class define for proxy/client side.
 *
 * Developer:
 *   wen.gu , 2021-09-09
 *
 * TODO:
 *
 ***************************************************************************/

/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#ifndef __ICPP_COM_PROXY_EVENT_H__
#define __ICPP_COM_PROXY_EVENT_H__
#include <functional>
#include <vector>
#include <memory>
#include <deque>

#include "icpp/com/types.h"
#include "icpp/com/message.h"
#include "icpp/com/result.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace icpp
{
namespace com
{
/**
 * 
 * - If it does exist, the binding shall call the DataReader’s get_subscription_matched_status() method next.
 *      – If the total_count attribute of the resulting SubscriptionMatchedStatus is greater than zero, GetSubscriptionState() shall return SubscriptionState = kSubscribed.
 *      – Otherwise, it shall return SubscriptionState = kSubscriptionPending.
 * - Else, if it does not exist—which indicates that either Subscribe() has never invoked or Unsubscribe() has been called before—GetSubscriptionState() shall return SubscriptionState = kNotSubscribed.

 * kSubscriptionPending: 
 * The Communication Management shall call the SubscriptionStateChangeHandler with the value kSubscriptionPending in the following cases:
 *  - the client subscribes to an event and the actual subscription does not happen immediately (e.g. due to a bus protocol)
 *  - the client is subscribed to an event and Communication Management has detected that the server instance is currently not available (due to restart, network problem or so) 
 */
enum class SubscriptionState : uint8_t 
{
    kSubscribed = 0,
    kNotSubscribed,
    kSubscriptionPending
};

/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/

/**
 * RequestPtr: must be a std::shared_ptr 
 */
template<class RequesterPtr, MessageType msg_type, MessageType subscribe_type, MessageType unsubscribe_type, EventId event_id, typename SampleType>
class COM_CLASS ProxyEventBase 
{
public:
    using SamplePtr = std::unique_ptr<SampleType const>;
    using EventReceiveHandler = std::function<void(SamplePtr&&)>;
    using SubscriptionStateChangeHandler = std::function<void(SubscriptionState)>;
    using SampleQueue = std::deque<SamplePtr>;
    using MessagePtr = Message::MessagePtr;
private:
    
    RequesterPtr requester_ptr_;    
    SampleQueue samples_;
    uint32_t max_sample_count_;
    SubscriptionStateChangeHandler state_change_handler_ = nullptr;
    EventReceiveHandler receive_handler_ = nullptr;
    SubscriptionState subscribe_state_ = SubscriptionState::kNotSubscribed;
public:
    ProxyEventBase(RequesterPtr requester_ptr)
    :requester_ptr_(requester_ptr)
    {
        /** todo something */
    }

    virtual ~ProxyEventBase()
    {
        /** todo something */
        unsubscribe();
    }
private:
    ProxyEventBase(const ProxyEventBase& other) = delete;
    ProxyEventBase& operator=(const ProxyEventBase& other) = delete;
public:
    /**
     * \brief The application expects the communication Management to subscribe the event.
     *
     * The Communication Management shall try to subscribe and resubscribe
     * until \see Unsubscribe() is called explicitly.
     * The error handling shall be kept within the Communication Management     
     *
     * The function returns immediately. If the user wants to get notified,
     * when subscription has succeeded, he needs to register a handler
     * via \see SetSubscriptionStateChangeHandler(). This handler gets
     * then called after subscription was successful.
     *
     * \param maxSampleCount maximum number of samples, which can be held.
     */
    virtual void subscribe(uint32_t max_sample_count = 1)
    {
        max_sample_count_ = max_sample_count;
        requester_ptr_->subscribe(msg_type, subscribe_type, event_id, [this](MessagePtr msg_ptr)
        {
            if (msg_ptr == nullptr)
            {
                return ;
            }

            Deserializer::DeserializerPtr deser = requester_ptr_->newDeserializer(msg_ptr->payload());
            SamplePtr sample_ptr(new SampleType);

            if (deser->deserialize(*(sample_ptr.get())) == false)
            {
                return ;
            }    

            if (this->receive_handler_)
            {
                this->receive_handler_(std::move(sample_ptr));
            }
            else
            {
                this->samples_.push_back(std::move(sample_ptr));

                if (this->samples_.size() >= this->max_sample_count_)
                {
                    this->onSubscriptionStateChange(SubscriptionState::kSubscriptionPending);
                    if (this->samples_.size() > this->max_sample_count_)
                    {
                        this->samples_.pop_front();
                    }
                }
            }
        });

        onSubscriptionStateChange(SubscriptionState::kSubscribed);
    }

    /**
     * \brief Query current subscription state.
     * 
     * \return Current state of the subscription.
     */
    SubscriptionState getSubscriptionState() const
    {
        return subscribe_state_;
    }

    /**
     * \brief Unsubscribe from the service.
     */
    void unsubscribe()
    {
        if (subscribe_state_ != SubscriptionState::kNotSubscribed)
        {
            requester_ptr_->unsubscribe(msg_type, unsubscribe_type, event_id);
            onSubscriptionStateChange(SubscriptionState::kNotSubscribed);           
        }

    }

    /**
     * \brief Get the number of currently free/available sample slots.
     *
     * \return number from 0 - N (N = count given in call to Subscribe())
     * or an IcppErrc in case of number of currently held samples
     * already exceeds the max number given in Subscribe().
     */
    Result<uint32_t> getFreeSampleCount() const noexcept
    {
        return max_sample_count_ - samples_.size();
    }
    /**
     * Setting a receive handler signals the Communication Management
     * implementation to use event style mode.
     * I.e. the registered handler gets called asynchronously by the
     * Communication Management as soon as new event data arrives for
     * that event. If the user wants to have strict polling behavior,
     * where no handler is called, NO handler should be registered.
     *
     * Handler may be overwritten anytime during runtime.
     *
     * Provided Handler needs not to be re-entrant since the
     * Communication Management implementation has to serialize calls
     * to the handler: Handler gets called once by the MW, when new
     * events arrived since the last call to getNewSamples().
     *
     * When application calls getNewSamples() again in the context of the
     * receive handler, MW must - in case new events arrived in the
     * meantime - defer next call to receive handler until after
     * the previous call to receive handler has been completed.
     */
    void setReceiveHandler(EventReceiveHandler handler)
    {
        receive_handler_ = handler;

        if (receive_handler_)
        {
            getNewSamples(receive_handler_);
        }
    }
    /**
     * Remove handler set by SetReceiveHandler()
     */
    void unsetReceiveHandler()
    {
        receive_handler_ = nullptr;
    }
    /**
     * Setting a subscription state change handler, which shall get
     * called by the Communication Management implementation as soon
     * as the subscription state of this event has changed.
     *
     * Communication Management implementation will serialize calls
     * to the registered handler. If multiple changes of the
     * subscription state take place during the runtime of a
     * previous call to a handler, the Communication Management
     * aggregates all changes to one call with the last/effective    
     * state.
     *
     * Handler may be overwritten during runtime.
     */
    void setSubscriptionStateChangeHandler(SubscriptionStateChangeHandler handler)
    {
        state_change_handler_ = handler;
    }

    /**
     * Remove handler set by SetSubscriptionStateChangeHandler()
     */
    void unsetSubscriptionStateChangeHandler()
    {
        state_change_handler_ = nullptr;
    }

    /**
     * \brief Get new data from the Communication Management buffers and provide it in callbacks to the given callable f.
     *
     * \pre Subscribe has been called before (and not be withdrawn by Unsubscribe)
     *
     * \param f callback, which shall be called with new sample. This callable has to fulfill signature 
     *     void(SamplePtr)
     *
     * \param maxNumberOfSamples upper bound of samples to be fetched from middleware buffers. Default value means "no restriction",
     *  i.e. all newly arrived samples  are fetched as long as there are free sample slots.
     *
     * \return Result, which contains the number of samples, which have been fetched and presented to user via calls to f or an
     *    IcppErrc in case of error (e.g. precondition not fullfilled)
     */
    template <typename F>
    Result<uint32_t> getNewSamples(F&& f, uint32_t maxNumberOfSamples = std::numeric_limits<uint32_t>::max())
    {
        if (f == nullptr)
        {
            return Result<uint32_t>::FromError(core::IcppErrc::BadParameter);
        }

        if (subscribe_state_ == SubscriptionState::kNotSubscribed)
        {
            return Result<uint32_t>::FromError(core::IcppErrc::InvalidStatus);
        }

        uint32_t sample_count = 0;
        for (auto & it: samples_)
        {
            if (sample_count >= maxNumberOfSamples)
            {
                break;        
            } 
            f(std::move(it));   
            samples_.pop_front();         
        }

        return Result<uint32_t>::FromValue(sample_count);
    }

private:
    void onSubscriptionStateChange(SubscriptionState sub_state)
    {
        if (sub_state != subscribe_state_)
        {
            subscribe_state_ = sub_state;
            if (state_change_handler_)
            {
                state_change_handler_(subscribe_state_);
            }
        }
    }
};

template<class RequesterPtr, MessageType msg_type, MessageType subscribe_type, MessageType unsubscribe_type, EventId event_id>
class COM_CLASS ProxyEventBase<RequesterPtr, msg_type, subscribe_type, unsubscribe_type, event_id, void> 
{
public:

    using EventReceiveHandler = std::function<void(void)>;
    using SubscriptionStateChangeHandler = std::function<void(SubscriptionState)>;
    using SampleQueue = std::deque<EventId>;
    using MessagePtr = Message::MessagePtr;
private:
    
    RequesterPtr requester_ptr_;    
    SampleQueue samples_;
    uint32_t max_sample_count_;
    SubscriptionStateChangeHandler state_change_handler_ = nullptr;
    EventReceiveHandler receive_handler_ = nullptr;
    SubscriptionState subscribe_state_ = SubscriptionState::kNotSubscribed;
public:
    ProxyEventBase(RequesterPtr requester_ptr)
    :requester_ptr_(requester_ptr)
    {
        /** todo something */
    }

    virtual ~ProxyEventBase()
    {
        /** todo something */
        unsubscribe();
    }
private:
    ProxyEventBase(const ProxyEventBase& other) = delete;
    ProxyEventBase& operator=(const ProxyEventBase& other) = delete;    
public:
    /**
     * \brief The application expects the communication Management to subscribe the event.
     *
     * The Communication Management shall try to subscribe and resubscribe
     * until \see Unsubscribe() is called explicitly.
     * The error handling shall be kept within the Communication Management     
     *
     * The function returns immediately. If the user wants to get notified,
     * when subscription has succeeded, he needs to register a handler
     * via \see SetSubscriptionStateChangeHandler(). This handler gets
     * then called after subscription was successful.
     *
     * \param maxSampleCount maximum number of samples, which can be held.
     */
    void subscribe(uint32_t max_sample_count = 1)
    {
        max_sample_count_ = max_sample_count;
        requester_ptr_->subscribe(msg_type, subscribe_type, event_id, [this](MessagePtr msg_ptr)
        {
            if (this->receive_handler_)
            {
                this->receive_handler_();
            }
            else
            {
                this->samples_.push_back(event_id);

                if (this->samples_.size() >= this->max_sample_count_)
                {
                    this->onSubscriptionStateChange(SubscriptionState::kSubscriptionPending);
                    if (this->samples_.size() > this->max_sample_count_)
                    {
                        this->samples_.pop_front();
                    }
                }
            }
        });

        onSubscriptionStateChange(SubscriptionState::kSubscribed);
    }

    /**
     * \brief Query current subscription state.
     * 
     * \return Current state of the subscription.
     */
    SubscriptionState getSubscriptionState() const
    {
        return subscribe_state_;
    }

    /**
     * \brief Unsubscribe from the service.
     */
    void unsubscribe()
    {
        if (subscribe_state_ != SubscriptionState::kNotSubscribed)
        {
            requester_ptr_->unsubscribe(msg_type, unsubscribe_type, event_id);
            onSubscriptionStateChange(SubscriptionState::kNotSubscribed);           
        }

    }

    /**
     * \brief Get the number of currently free/available sample slots.
     *
     * \return number from 0 - N (N = count given in call to Subscribe())
     * or an IcppErrc in case of number of currently held samples
     * already exceeds the max number given in Subscribe().
     */
    Result<uint32_t> getFreeSampleCount() const noexcept
    {
        return max_sample_count_ - samples_.size();
    }
    /**
     * Setting a receive handler signals the Communication Management
     * implementation to use event style mode.
     * I.e. the registered handler gets called asynchronously by the
     * Communication Management as soon as new event data arrives for
     * that event. If the user wants to have strict polling behavior,
     * where no handler is called, NO handler should be registered.
     *
     * Handler may be overwritten anytime during runtime.
     *
     * Provided Handler needs not to be re-entrant since the
     * Communication Management implementation has to serialize calls
     * to the handler: Handler gets called once by the MW, when new
     * events arrived since the last call to getNewSamples().
     *
     * When application calls getNewSamples() again in the context of the
     * receive handler, MW must - in case new events arrived in the
     * meantime - defer next call to receive handler until after
     * the previous call to receive handler has been completed.
     */
    void setReceiveHandler(EventReceiveHandler handler)
    {
        receive_handler_ = handler;

        if (receive_handler_)
        {
            getNewSamples(receive_handler_);
        }
    }
    /**
     * Remove handler set by SetReceiveHandler()
     */
    void unsetReceiveHandler()
    {
        receive_handler_ = nullptr;
    }
    /**
     * Setting a subscription state change handler, which shall get
     * called by the Communication Management implementation as soon
     * as the subscription state of this event has changed.
     *
     * Communication Management implementation will serialize calls
     * to the registered handler. If multiple changes of the
     * subscription state take place during the runtime of a
     * previous call to a handler, the Communication Management
     * aggregates all changes to one call with the last/effective    
     * state.
     *
     * Handler may be overwritten during runtime.
     */
    void setSubscriptionStateChangeHandler(SubscriptionStateChangeHandler handler)
    {
        state_change_handler_ = handler;
    }

    /**
     * Remove handler set by SetSubscriptionStateChangeHandler()
     */
    void unsetSubscriptionStateChangeHandler()
    {
        state_change_handler_ = nullptr;
    }

    /**
     * \brief Get new data from the Communication Management buffers and provide it in callbacks to the given callable f.
     *
     * \pre Subscribe has been called before (and not be withdrawn by Unsubscribe)
     *
     * \param f callback, which shall be called with new sample. This callable has to fulfill signature 
     *     void(SamplePtr)
     *
     * \param maxNumberOfSamples upper bound of samples to be fetched from middleware buffers. Default value means "no restriction",
     *  i.e. all newly arrived samples  are fetched as long as there are free sample slots.
     *
     * \return Result, which contains the number of samples, which have been fetched and presented to user via calls to f or an
     *    IcppErrc in case of error (e.g. precondition not fullfilled)
     */
    template <typename F>
    Result<uint32_t> getNewSamples(F&& f, uint32_t maxNumberOfSamples = std::numeric_limits<uint32_t>::max())
    {
        if (f == nullptr)
        {
            return Result<uint32_t>::FromError(core::IcppErrc::BadParameter);
        }

        if (subscribe_state_ == SubscriptionState::kNotSubscribed)
        {
            return Result<uint32_t>::FromError(core::IcppErrc::InvalidStatus);
        }

        uint32_t sample_count = 0;
        for (auto & it: samples_)
        {
            if (sample_count >= maxNumberOfSamples)
            {
                break;        
            } 
            f(std::move(it));   
            samples_.pop_front();         
        }

        return Result<uint32_t>::FromValue(sample_count);
    }

private:
    void onSubscriptionStateChange(SubscriptionState sub_state)
    {
        if (sub_state != subscribe_state_)
        {
            subscribe_state_ = sub_state;
            if (state_change_handler_)
            {
                state_change_handler_(subscribe_state_);
            }
        }
    }
};




template<class RequesterPtr, EventId event_id, typename SampleType>
class COM_CLASS ProxyEvent: public ProxyEventBase<RequesterPtr, MessageType::kNotification, MessageType::kSubscribe, MessageType::kUnsubscribe, event_id, SampleType>
{
public:
    ProxyEvent(RequesterPtr requester_ptr)
    :ProxyEventBase<RequesterPtr, MessageType::kNotification, MessageType::kSubscribe, MessageType::kUnsubscribe, event_id, SampleType>(requester_ptr)
    {
        /** todo something */
    }

};

} /** namespace com */
} /** namespace icpp */

#endif /** !__ICPP_COM_PROXY_EVENT_H__ */

